package com.mlming.springboot.controller;

import com.mlming.springboot.consts.MallConst;
import com.mlming.springboot.form.UserLoginForm;
import com.mlming.springboot.form.UserRegisterForm;
import com.mlming.springboot.pojo.User;
import com.mlming.springboot.service.UserService;
import com.mlming.springboot.util.JWTUtil;
import com.mlming.springboot.vo.ResponseVo;
import com.mlming.springboot.vo.UserVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;

@RestController
public class UserController {
    @Autowired
    private UserService userService;
    /**
     * 注册
     */
    // 使用@Valid与@NotXXX注解方便对内部属性进行判断,可以使用BindingResult类来接收错误信息
    @RequestMapping("/user/register")
    public ResponseVo register(@Valid UserRegisterForm userRegisterForm) {
        // 复制抽取属性对象的属性到User对象里
        User user = new User();
        BeanUtils.copyProperties(userRegisterForm,user);
        // 调用Service层的注册逻辑方法
        ResponseVo<User> result = userService.register(user);
        // 如果注册成功:
        if(result.getStatus() == 0) {
            // 获取到用户数据:
            User data = result.getData();
            // 返回包含着token + user数据的UserVo数据
            UserVo userVo = this.getUserVoWithTokenAndUserData(data);
            return ResponseVo.success(userVo);
        }
        return result;
    }

    /**
     * 登录
     */
    // 使用@Valid与@NotXXX注解方便对内部属性进行判断,可以使用BindingResult类来接收错误信息
    @PostMapping("/user/login")
    public ResponseVo login(@Valid UserLoginForm userLoginForm,HttpSession session) {
        // 调用Service层的登录逻辑方法
        ResponseVo<User> result = userService.login(userLoginForm.getUsername(), userLoginForm.getPassword());
        // 判断登录是否成功,// 如果成功则设置session,保存登录状态
        // 如果成功则返回JWT给前端进行保存,用于之后访问服务器
        if(result.getStatus() == 0) {
            // session.setAttribute(MallConst.CURRENT_USER,result.getData());
            UserVo userVo = this.getUserVoWithTokenAndUserData(result.getData());
            // 返回包含着token + user数据的UserVo数据
            return ResponseVo.success(userVo);
        }
        return result;
    }

    /**
     * 登出
     */
    @PostMapping("/user/logout")
    public ResponseVo logout() {
        // 由于之前登陆状态会由拦截器进行拦截,所以
        // session.removeAttribute(MallConst.CURRENT_USER);
        // JWT是存在客户端的, 要实现其的下线功能, 就算在后台把JWT修改各种参数都是没用的,
        // 因为前端只要不存新的或者有人拿到之前的,就还是能访问, 因为JWT的验证只从其本身出发
        // 只有两种方法解决:
        // 1) 依靠redis存储, 这个又有多种验证方式:
        //    例如: - 黑名单: 直接把整个token存进去, 然后来token时判断一下是否在黑名单里, 在的话, 说明已注销或者修改
        //         - 副本: 也是直接存入token, 然后来token判断是否在, 不在则不通过
        //         - 版本号: 把userId与版本号存入redis, 每次来token时, 取出userId与版本号与redis的对比, 如果不相等就不通过
        // 但是1)虽然可以实现后端的强制下线, 但是他再一次把东西存在了后端, 这就和一开始放弃session拥抱jwt的本意 以及 JWT无状态原则
        // 2) 后端不管, 全权交给前端处理, 即: 前端自己把每次会过来的token删除
        // 虽然这个坚持住了JWT的原则, 但是并不能实现真正的下线

        // 这里就采用2)吧
        return ResponseVo.success();
    }

    /**
     * 查询登录状态的用户信息
     */
    @GetMapping("/user")
    public ResponseVo userInfo(HttpServletRequest request) {
        // 由于之前登陆状态会由拦截器进行拦截,所以
        // User user = (User) session.getAttribute(MallConst.CURRENT_USER);
        // 从JWT中获取到id值,查询用户
        String token = request.getHeader(MallConst.TOKEN_NAME);
        Integer id = Integer.valueOf(JWTUtil.getFieldValuePayLoad(token, MallConst.USER_ID_IN_PAYLOAD));
        return userService.getUser(id);
    }

    // 生成token
    private UserVo getUserVoWithTokenAndUserData(User data) {
        // 设置payload
        Map<String,String> payloadMap = new HashMap<>();
        payloadMap.put(MallConst.USER_ID_IN_PAYLOAD, String.valueOf(data.getId()));
        payloadMap.put(MallConst.USER_NAME_IN_PAYLOAD,data.getUsername());
        // 调用JWTUtil工具类生成token,并设置vo返回类型
        String token = JWTUtil.getToken(payloadMap);
        UserVo userVo = new UserVo();
        BeanUtils.copyProperties(data,userVo);
        userVo.setToken(token);
        return userVo;
    }
}
